Mouse & Keyboard Management "The ass loaded with gold still eats thistle" German proverb Introduction The GOLDKEY unit contains all the mouse and keyboard management routines needed to support sophisticated DOS applications. The unit includes the following: - Functions which wait for user input by keyboard or mouse. - Support for mouse double-clicks, as well as keyboard mouse combinations such as Alt-Left Button. - Routines to hide, display, confine and move the mouse cursor. - Routines to get and set the state of the toggle keys, e.g. Caps Lock, Num Lock, etc. - Procedures to stuff keystrokes and mouse actions in the application's keyboard buffer. - Support for user-defined call back functions. - Routines to change the keyboard repeat rate. What Did the User Press (or Click)? The primary purpose of GOLDKEY is to provide a flexible way to pause for user mouse or keyboard activity and make it easy to then find out what the user did! Gold uses three basic variables to define a single user input: the keystroke or mouse action, the mouse X coordinate and the mouse Y coordinate. These three variables are defined in the KeyVars record as LastKey, LastX and LastY. LastKey is a word-sized variable which is updated with a number to indicate the key pressed. For example, if Lastkey has a value of 27, the user pressed the Esc key, and if LastKey has a value of 520, the user must have clicked the left mouse button. The values in LastX and LastY indicate the location of the mouse cursor when the button was clicked. Refer to the table on page 5-3 for a complete list of the supported keystrokes and mouse clicks. TTT5 did not support a visible (free floating) mouse and life was much simpler! In these good old days, keystrokes were defined in a byte-sized variable. In Gold, a single keystroke or mouse action is stored in a word-sized variable (KeyVars.LastKey). This change allows a much wider range of keystrokes to be reported, and makes Gold more flexible for international users. Pausing for User Input At the heart of Gold's input management routines are the GetInput and GetInputRel procedures. The GetInput procedure pauses program execution and waits for the user to press a key combination or press down a mouse button. As soon as the user has pressed a key (combination) and pressed down a mouse button, the KeyVars.LastKey, KeyVars.LastX and KeyVars.LastY variables are updated with the appropriate values, and program execution continues. A important point to note is that GetInput will return as soon as a mouse button is pressed down. In other words, this function doesn't wait for the user to release the mouse button. By responding immediately to a mouse down, an application can implement "dragging support" where some task has to be performed before the button is released. Gold, for example, uses this procedure in the window dragging routines. However, if you don't need to support any custom dragging operations, call the GetInputRel procedure. This procedure is very similar to GetInput, except that the procedure only returns from a mouse click when the user has released the mouse button. With respect to the keyboard, GetInput and GetInputRel are identical. After calling GetInput or GetInputRel, the program should test the value of KeyVars.LastKey to see what the user did. If the value is mouse-related, the program should check the values of KeyVars.LastX and KeyVars.LastY to see where the mouse cursor was when the button was clicked. The following table details all the codes returned in KeyVars.LastKey: Keyboard codes ÉÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍËÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ» º Key Norm Shft Alt Ctrl º Key Norm Shft Alt Ctrl º ÌÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÎÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ͹ º a 97 65 286 1 º F1 315 340 260 350 º º b 98 66 304 2 º F2 316 341 361 351 º º c 99 67 302 3 º F3 317 342 362 352 º º d 100 68 288 4 º F4 318 343 363 353 º º e 101 69 274 5 º F5 319 344 364 354 º º f 102 70 289 6 º F6 320 345 365 355 º º g 103 71 290 7 º F7 321 346 366 356 º º h 104 72 291 8 º F8 322 347 367 357 º º i 105 73 279 9 º F9 323 348 368 358 º º j 106 74 292 10 º F10 324 349 369 359 º º k 107 75 293 11 º F11 389 391 395 393 º º l 208 76 294 12 º F12 390 392 396 394 º º m 109 77 306 13 º Enter 13 13 284 10 º º n 110 78 305 14 º BkSp 8 8 270 127 º º o 111 79 280 15 º Up 328 428 408 397 º º p 112 80 281 16 º Down 336 436 416 401 º º q 113 81 272 17 º Left 331 431 411 371 º º r 114 82 275 18 º Right 333 433 413 372 º º s 115 83 287 19 º End 335 435 415 373 º º t 116 84 276 20 º Home 327 427 407 375 º º u 117 85 278 21 º PgUp 329 429 409 388 º º v 118 86 303 22 º PgDn 337 437 417 374 º º w 119 87 273 23 º Ins 338 261 418 260 º º x 120 88 301 24 º Del 339 263 419 262 º º y 121 89 277 25 º Tab 9 271 421 404 º º z 122 90 300 26 º Esc 27 27 257 27 º º 1 ! 49 33 376 º , < 44 60 307 º º 2 @ 50 64 377 259 º . > 46 62 308 º º 3 # 51 35 378 º / ? 47 63 309 º º 4 $ 52 36 379 º ; : 59 58 295 º º 5 % 53 37 380 º ' " 39 34 296 º º 6 ^ 54 94 381 30 º [ { 91 123 282 27 º º 7 & 55 38 382 º ] } 93 125 283 29 º º 8 * 56 42 383 º \ | 92 124 299 28 º º 9 ( 57 40 384 º º º 0 ) 48 41 385 º º º - _ 45 95 386 31 º º º = + 61 43 387 º º ÈÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÊÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍͼ Mouse Codes Mouse Action Code Mouse Action Code LeftMouseDown 500 LeftMouseClick 520 LeftMouseDown+Alt 501 LeftMouseClick+Alt 521 LeftMouseDown+Ctrl 502 LeftMouseClick+Ctrl 522 LeftMouseDown+Shift 503 LeftMouseClick+Shift 523 RightMouseDown 504 RightMouseClick 524 RightMouseDown+Alt 505 RightMouseClick+Alt 525 RightMouseDown+Ctrl 506 RightMouseClick+Ctrl 526 RightMouseDown+Shift 507 RightMouseClick+Shift 527 CenterMouseDown 508 CenterMouseClick 528 CenterMouseDown+Alt 509 CenterMouseClick+Alt 529 CenterMouseDown+Ctrl 510 CenterMouseClick+Ctrl 530 CenterMouseDown+Shift 511 CenterMouseClick+Shift 531 LeftMouseDoubleClick 540 LeftDoubleClickonList 560 RightMouseDoubleClick 544 CenterMouseDoubleClick 548 Window Codes Window Action Code Close icon selected 600 Window moved or dragged 601 Window zoomed or stretched 602 Up arrow on V scrollbar selected 610 Down arrow on V scrollbar selected 611 Left arrow on H scrollbar selected 612 Right arrow on H scrollbar selected 613 Click on body of V scrollbar 614 Clicked on body of H scrollbar 615 Refer to Chapter 4 for a full explanation of the window codes, and when they are generated. Run the demo program DEMKY1.PAS to interactively see the key codes used by Gold. A Basic Input Loop The following program fragment illustrates a basic input loop which pauses for user input and then calls an appropriate subroutine: Finished := false repeat GetInputRel; case KeyVars.LastKey of 301,113,81: Finished := true; {Alt-X,q,Q} 315: HelpEm; {F1} 337: ScrollDown20; {PgDn} 329: ScrollUp20; {PgUp} 287: SaveIt; {Alt-S} 520: begin {Left button click} if (KeyVars.LastX <= 10) and (KeyVars.LastY = HardVars.Depth) then Finished := true; end; end; until Finished; Ignoring the Mouse If you are not interested in mouse activity and want to wait for the user to hit a standard key or key combination, you can use the function GetKey which is defined as follows: GetKey:char; Waits for the user to press a key or key combination which has a value in the range 13 to 255. The function returns the character value of the input. Mouse activity is ignored. Behind the scenes, GetKey uses GetInput and so the KeyVars variables LastKey, LastX and LastY are updated. Setting the Maximum Wait Time for Input Gold includes the procedure GetInputWait to allow programs to run with or without user input, and is defined as follows: GetInputWait(WaitTime:longint); Waits for user input via the mouse or keyboard. GetInputWait is very similar to GetInput, except that a maximum wait time is specified in milliseconds. The user input is recorded in the KeyVars variables LastKey, LastX and LastY. If WaitTime expires before user input is received, the procedure terminates and LastKey is set to zero. If a wait time of zero is specified, GetInputWait will not time out, i.e. it will wait indefinitely for user input. Controlling and Managing the Mouse Gold makes extensive use of the mouse in everything from menus to input forms. As a developer, you gain all this power without having to write any mouse-specific code. However, if you want to write some custom routines which need to access the mouse, you can take advantage of the following mouse routines: MouseInstalled:boolean; Returns TRUE if a mouse and mouse driver are installed, otherwise FALSE is returned. MouseHardwareReset; Uses a hardware interrupt to reset the mouse to its standard defaults. In 99% of hardware setups, a software reset (discussed next) is faster and has the same effect. MouseSoftwareReset; Uses a software interrupt to set the mouse to its default configuration. This function is called automatically when a program (which uses GOLDKEY) is started. MouseShow(On:boolean); When passed TRUE, the mouse is made visible, otherwise the mouse is hidden. MouseMove(X,Y: integer); Moves the mouse to the specified (X,Y) coordinate. Note that local window or screen coordinates have no effect on this procedure -- coordinates are always treated as global. MouseConfine(X1,Y1,X2,Y2:integer); Defines a rectangular region of the screen where the mouse can move -- the user will not be able to move the mouse cursor beyond the specified region. MousePos(var X,Y: byte); Updates the passed parameters with the current location of the mouse cursor. Note that local window or screen coordinates have no effect on this procedure -- coordinates are always reported as globals. MouseReleased(Button: integer; var X,Y: byte): byte; Returns the number of times the specified button has been released since the function was last called. Button values of 0, 1 and 2 represent the left, right and middle buttons, respectively. The variables X and Y are updated with the mouse cursor coordinates the last time the button was released. MousePressed(Button: integer; var X,Y: byte): byte; Returns the number of times the specified button has been pressed since the function was last called. Button values of 0, 1 and 2 represent the left, right and middle buttons, respectively. The variables X and Y are updated with the mouse cursor coordinates the last time the button was pressed. MouseInZone(X1,Y1,X2,Y2: byte):boolean; Returns TRUE if the mouse cursor is situated within the specified global coordinates. MouseStatus(var L,C,R:boolean; var X,Y : byte); Updates the L, C and R boolean variables to TRUE if any of the left, center or right buttons are depressed, and updates the variables X and Y with the current mouse location. MouseStatusWin(var L,C,R: boolean; var X,Y: byte); Operates like MouseStatus, except that the X and Y coordinates are local to the window with focus. MouseStyle(OrdChar,Attr: byte); Sets the mouse cursor to the character represented by the ASCII value OrdChar. The second parameter controls the display attribute of the mouse cursor. If a zero is specified, Gold uses the standard mouse device driver technique, which changes color based on the underlying screen attribute to ensure maximum contrast. MouseRelease; Waits for all mouse buttons to be released. Run the demo program DEMKY2.PAS to see the mouse routines in action. Mouse-Related Elements of KeyVars The following elements of the global record KeyVars affect the mouse functions: MouseActive The variable KeyVars.MouseActive is set to TRUE (at program start-up) if a mouse is installed. Gold can be forced to ignore the mouse by setting this variable to FALSE. DoubleDelay Gold has to differentiate between two single clicks of the mouse and one double click. To use an old gag, the difference is "timing". If two single mouse clicks occur within a specified (short) time period, a single click and a double click are returned. The variable KeyVars.DoubleDelay specifies, in milliseconds, the maximum elapsed time allowed between two mouse clicks if the second click is to be reported as a double click. The default is 350. This variable can be changed as desired. RightHanded The de facto standard in the industry is for the left mouse button to be the "do it" or "enter" button, and this is Gold's default. By setting the variable KeyVars.RightHanded to TRUE, an application will behave as though the right mouse button is the do-it button. If your code is written with the left mouse codes as the standard, a simple change to this variable is all that's needed to give right button support. InitScrollDelay, ScrollDelay When a user clicks on a scroll bar (in a window, list, form, etc.) there is a momentary delay before scrolling occurs. The variable KeyVars.InitScrollDelay sets the duration of the initial delay, in milliseconds, and the variable KeyVars.ScrollDelay controls the subsequent scrolling rate during scroll-bar operations. The defaults are 350 and 50 respectively. These variables can be changed as desired. Accessing Ctrl, Alt and Shift GOLDKEY supports a wide variety of special key combinations like Alt-Backspace and Ctrl-PgUp. Sometimes, however, you may want to know if one of the shifting keys is being pressed on its own. GetInput and GetInputWait will not respond when Ctrl, Alt or either Shift key is pressed. GOLDKEY does, however, provide the following five functions to let you check the status of the special shifting keys: KeyShiftPressed: boolean; Returns TRUE if either Shift key is being held down. KeyRightShiftPressed: boolean; Returns TRUE if the right Shift key is being held down. KeyLeftShiftPressed: boolean; Returns TRUE if the left Shift key is being held down. KeyCtrlPressed:boolean; Returns TRUE if a Ctrl key is being held down. KeyAltPressed:boolean; Returns TRUE if an Alt key is being held down. Accessing Num, Scroll and Caps Lock GOLDKEY provides the following routines for determining and changing the status of the Num Lock, Scroll Lock and Caps Lock keys: KeyGetNum:boolean; Returns TRUE if the Num Lock key is on. KeyGetScroll:boolean; Returns TRUE if the Scroll Lock key is on. KeyGetCaps:boolean; Returns TRUE if the Caps Lock key is on. KeySetNum(On:boolean); Pass TRUE to turn on the Num Lock key, or FALSE to turn it off. KeySetScroll(On:boolean); Pass TRUE to turn on the Scroll Lock key, or FALSE to turn it off. KeySetCaps(On:boolean); Pass TRUE to turn on the Caps Lock key, or FALSE to turn it off. Run the demo program DEMKY3.PAS to see how to access the shifting and locking keys. Setting the Keyboard Repeat Rate PC keyboards support a typematic effect, i.e. holding a key pressed down has the same effect as pressing a key multiple times. By default, a key must be held down for about half a second before the repetition commences, and thereafter about ten characters per second are sent. Most users think this repeat rate is too slow (people west of Oklahoma excepted) and Gold provides a way of changing it. The following three procedures alter the typematic rate on systems with BIOS dated December 1985 or later: KeySetFast; Sets the typematic rate to 30 characters per second. This is fast enough even for a New Yorker. KeySetSlow; Sets the typematic rate to 5 characters per second. This is slow enough for the Post Office (just joking). KeySetRepeatRate(Delay,Rate:byte); This procedure allows you to explicitly set the typematic rate. The first parameter accepts a value between 1 and 4 and indicates the number of quarter seconds delay before the key commences repeating. The second parameter indicates the repeat rate and accepts a value between 0 and 31. The following table shows the repeat rate parameter along with the resultant repeat rate: Val Rate Val Rate Val Rate Val Rate Val Rate 0 30.0 7 16.0 14 8.6 21 4.6 28 2.5 1 26.7 8 15.0 15 8.0 22 4.3 29 2.3 2 24.0 9 13.3 16 7.5 23 4.0 30 2.1 3 21.8 10 12.0 17 6.7 24 3.7 31 2.0 4 20.0 11 10.9 18 6.0 25 3.3 5 18.5 12 10.0 19 5.5 26 3.0 6 17.1 13 9.2 20 5.0 27 2.7 Stuffing the Keyboard/Mouse Buffer GOLDKEY uses an internal buffer for storing keystrokes and mouse clicks. The sole purpose of this buffer is to enable you to force characters into the buffer using the KeyStuffBuffer... procedures. The GetInput and GetInputWait functions check to see if there any characters in the buffer before checking the physical mouse and keyboard. By default the size of the buffer is 30 characters, but this can easily be modified by changing the value of the GStuffBufferSize constant at the top of the GOLDKEY.PAS file. Listed below is a description of the buffer-related procedures: KeyStuffBuffer(W:word); Adds a keystroke to the keyboard buffer. The parameter W is the word value of the keystroke as detailed in the table starting on page 6.3. KeyStuffBufferMouse(W:word;X,Y:byte); Use this function to add a mouse click to the keyboard buffer. The W parameter identifies the mouse action and should be in the range 520 to 560. X and Y identify the location of the mouse cursor corresponding with the action. KeyStuffBufferStr(Str:string); Stuffs characters (with ASCII values in the range 0 to 255) into the keyboard buffer. This procedure is provided as a convenience to save making multiple calls to KeyStuffBuffer (one call for every character in the string). KeyBufferSpace:word; Returns the amount of free space available in the keyboard buffer. If the buffer is full, any attempt to stuff additional keystrokes will be ignored. KeyFlushBuffer; Erases all the entries in the application's keyboard buffer. KeyFlushDOSBuffer; Erases all the entries in the DOS keyboard buffer. The keyboard buffer routines form the framework of a simple but elegant macro facility. The demo file DEMKY5.PAS implements a macro system and is discussed further in the (later) section Using Keyboard Hooks. Goodbye Keypress, Hello GkeyPressed The standard Borland Pascal function KeyPressed returns TRUE if there is a character waiting in DOS's keyboard buffer. In a Gold application, you should use GKeyPressed (for Gold KeyPressed) instead. GKeyPressed checks the internal application keyboard buffer as well as the DOS buffer. To parody Nike, "Just use it". If you want to check and see whether a key has been pressed, or whether a mouse button is currently pressed, call the function KeyorMousePressed. It functions like GkeyPressed but additionally checks the state of the mouse buttons. Using Keyboard Hooks Hooks provide a way for a developer to integrate custom routines into the heart of Gold. GOLDKEY supports two primary types of keyboard hooks. Gold can call a procedure while the program is idle and waiting for the user to press a key; this type of hook is known as an idle hook (or a Post Office hook). Similarly, you can assign a procedure which will be called every time a character is pressed or the mouse is clicked; this type of hook is known as a pressed hook. Using an Idle Hook An idle hook is an external procedure which is called repeatedly while a program is waiting for the user to input data. A good use for an idle hook is to display a clock, or to show the status of the shifting keys. All you have to do is create a procedure following some specific rules, and then call the procedure AssignIdleHook to instruct Gold to call the procedure during idle time. For a procedure to be eligible as an idle hook it must adhere to the following rules: The procedure must be declared as a far procedure at the root level. Refer to section Understanding Hooks in Chapter 3 for further information. The procedure must be declared with no (nada/zero/none) passed parameters. The following procedure declaration follows these rules: {$F+} procedure MyIdleHook; begin {some code} end; {MyIdleHook} {$F-} The following procedure is then called to instruct Gold to call your procedure while the application is waiting for input: AssignIdleHook(Hook:KeyIdleHook); Instructs Gold to call the specified procedure while pausing for user input. For example, the above procedure would be assigned with the following statement: AssignIdleHook(MyIdleHook); The hooked procedure will be continually called while your program is waiting for user input, so make sure that the procedure is compact and doesn't consume too many clock cycles. If your procedure involves too many tasks, the program will slow down considerably during input. If you want to call an extended task, like a background print program, you must continually checked the GkeyPressed function and suspend your procedure when it returns TRUE. If subsequently, you want to remove the idle hook, execute the following: AssignIdleHook(NoKeyIdleHook); The demo file DEMKY4.PAS illustrates the use of an idle hook to display a ticking clock. Using a Pressed Hook A pressed hook is a procedure which is called every time a key is pressed or a mouse button is clicked. The hooked procedure is called before the key is processed, i.e. before the character is returned by GetInput or GetInputWait. The hook is particularly useful for trapping special keys like F1 for help, or for building keyboard macros. All you have to do is create a procedure following some specific rules, and then call the AssignPressedHook procedure to instruct Gold to call your procedure every time a key is pressed. For a procedure to be eligible as a pressed hook it must adhere to the following rules: The procedure must be declared as a far procedure at the root level. Refer to the section Understanding Hooks in Chapter 3 for further information. The procedure must be declared with one variable parameter of type word, and two variable parameters of type byte. These parameters identify the user input, i.e. the Key, X and Y. The following procedure declaration follows these rules: {$F+} procedure MyPressedHook(var Code:word;var X,Y:byte); begin {some code} end; {MyPressedHook} {$F-} The following procedure is then called to instruct Gold to call your procedure after each input: AssignPressedHook(Hook:KeyPressedHook); Instructs Gold to call the specified procedure before processing each user input. If, subsequently, you want to remove the pressed hook, execute the following: AssignPressedHook(NoKeyPressedHook); The demo file DEMKY5.PAS shows how a pressed hook can be used to provide macro keyboard support. Other Neat Stuff Here are a few more procedures included in GOLDKEY (we couldn't decide where else to put them): ExtendedKeyBoard:boolean; Returns TRUE if the host system has an extended keyboard, i.e. has 101 or more keys. KeySetClicking(Clicking : boolean); Call this procedure with a TRUE parameter if you want Gold to make a tactile clicking noise every time a key is pressed, or pass FALSE to turn off clicking. WordToChar(W:word):char; Gold uses a word-sized variable KeyVars.Lastkey to record user input. Use this function translate the word-type keystroke to a character. If the passed parameter is greater than 255, a #0 is returned.